package keter.combofs.api.adapters;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.s3.model.*;
import com.google.common.io.ByteStreams;
import keter.combofs.api.s3.ComboS3;
import keter.combofs.config.S3Config;
import keter.combofs.id.IdMaker;
import keter.combofs.model.ComboFile;
import keter.combofs.model.FileMeta;
import keter.combofs.util.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.Map;

/**
 * Created by gulx@neusoft.com on 2019/11/11.
 */
public class ComboFsS3Adapter implements ComboS3 {

    private static final Logger logger = LoggerFactory.getLogger(ComboFsS3Adapter.class);
    @Value("${combofs.s3.bucket:nonexist}")
    protected String bucket;

    @Autowired S3Config s3Config;
    @Autowired private IdMaker sequence;

    @Override
    public ComboFile getComboFile(Long id) throws IOException {
        S3Object object = getFileObject(id.toString());
        ComboFile file =  ComboFile.build(id,id.toString(),convertS3ToFileMeta(object.getObjectMetadata()), getFileBytes(object),object.getObjectMetadata().getETag());
        object.close();
        return file;
    }

    @Override
    public ComboFile getComboFile(String fileUri) throws IOException {
        S3Object object = getFileObject(fileUri);
        ComboFile file =  ComboFile.build(fileUri,convertS3ToFileMeta(object.getObjectMetadata()), getFileBytes(object),object.getObjectMetadata().getETag());
        object.close();
        return file;
    }

    @Override
    public byte[] getBytes(Long id) throws IOException {
        if(ComboFile.EMPTY_ID.equals(id)){
            throw new RuntimeException("该文件使用URI保存，请使用getBytes(String fileUri)方法进行文件读取！");
        }
        return getBytes(id.toString());
    }

    @Override
    public byte[] getBytes(String fileUri) throws IOException {
        return getFileBytes(getFileObject(fileUri));
    }

    @Override
    public ComboFile save(byte[] b) throws IOException {
        return save(b,FileMeta.build());
    }

    @Override
    public ComboFile save(InputStream in) throws IOException {
        return save(ByteStreams.toByteArray(in));
    }

    @Override
    public ComboFile save(byte[] b, FileMeta fileMeta) throws IOException {
        Long id = sequence.makeId();
        return saveFile(id, b, fileMeta);
    }

    @Override
    public void save(byte[] b, String fileUri) throws IOException {
         save(b,fileUri,null);
    }

    @Override
    public void save(InputStream in, String fileUri) throws IOException {
         save(ByteStreams.toByteArray(in),fileUri);
    }

    @Override
    public ComboFile save(byte[] b, String fileUri, FileMeta fileMeta) throws IOException {
        writeFile(b, fileUri, fileMeta);
        return ComboFile.build(fileUri, fileMeta);
    }

    @Override
    public boolean delete(Long id) {
        logger.info("删除文件: id={}",id);
        delete(id.toString());
        return true;
    }

    @Override
    public boolean delete(String fileUri) {
        logger.info("删除文件: fileUri={}",fileUri);
        s3Config.client().deleteObject(bucket, fileUri);
        return true;
    }

    private ComboFile saveFile(Long id, byte[] b, FileMeta fileMeta) throws IOException {
        writeFile(b, id.toString(), fileMeta);
        return ComboFile.build(id, id.toString(), fileMeta, null);
    }

    private void writeFile(byte[] b, String fileUri, FileMeta meta) throws IOException {
        // 分割符替换
        fileUri = StringUtils.replace(fileUri,"\\","/");
        if(fileUri.startsWith("/")){
            fileUri = StringUtils.substringAfter(fileUri,"/");
        }
        ObjectMetadata metadata = convertFileMetaToS3(meta);
        // 设置文件长度
        metadata.setContentLength(b.length);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, fileUri,  new ByteArrayInputStream(b), metadata);
        // 文件写入
        s3Config.client().putObject(putObjectRequest);
        logger.info("文件:[{}]写入成功!",fileUri);
    }

    private ObjectMetadata convertFileMetaToS3(FileMeta meta){
        ObjectMetadata metadata = new ObjectMetadata();
        if(meta == null){
            return metadata;
        }
        meta.getData().entrySet().stream().forEach(o -> {
            Map.Entry entry = (Map.Entry) o;
            metadata.addUserMetadata((String) entry.getKey(), (String) entry.getValue());
        });
        return metadata;
    }

    private FileMeta convertS3ToFileMeta(ObjectMetadata meta){
        FileMeta fileMeta = new FileMeta();
        if(meta == null){
            return fileMeta;
        }
        meta.getUserMetadata().entrySet().stream().forEach(
            o -> fileMeta.add(o.getKey(),o.getValue())
        );
        fileMeta.add("contentLength",String.valueOf(meta.getContentLength()));
        fileMeta.add("eTag",String.valueOf(meta.getETag()));
        return fileMeta;
    }

    private  byte[] getFileBytes(S3Object object) throws IOException {
        S3ObjectInputStream inputStream = object.getObjectContent();
        byte[] bytes =  ByteStreams.toByteArray(inputStream);
        inputStream.close();
        return bytes;
    }

    private  S3Object getFileObject(String fileKey) throws FileNotFoundException {
        S3Object file;
        try {
            //由根存储桶和会员ID构成实际存储地址
            file = s3Config.client().getObject(bucket,fileKey);
        } catch (AmazonS3Exception e) {
            if(404 == e.getStatusCode()){
                logger.error("S3读取失败:文件bucket=[{}], key=[{}]不存在！",bucket,fileKey);
                throw new FileNotFoundException("文件读取失败:["+fileKey+"]不存在！");
            }
            throw new AmazonServiceException("S3文件读取失败！");
        }
        return file;
    }


    @Override
    public URL getUrl(String fileKey) {
        return getUrl(fileKey, DateUtil.expiredMinutes(10));
    }

    @Override
    public URL getUrl(String fileKey, Date expired) {
       return s3Config.client().generatePresignedUrl(bucket,fileKey,expired);
    }

//    @Override
//    public ObjectMetadata getMeta(String fileKey) {
//        return s3Config.client().getObjectMetadata(bucket,fileKey);
//    }
}
